package com.lblin.method.util.monitor;

import java.io.File;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.JavaFileManager.Location;
import javax.tools.StandardLocation;

import com.lblin.method.adt.CheckMethodUser;
import com.lblin.method.adt.LoadClass;
import com.xiaoleilu.hutool.io.FileUtil;



@SupportedAnnotationTypes(value= {"com.lblin.method.annotation.HaveMethod","com.lblin.method.annotation.Check"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MethodMonitorProcessing extends AbstractProcessor{
	
	private String java_main_dir = "src/main/java";
	
	private String override_dir = "methodClean.apt";
	
	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		Filer filer = this.processingEnv.getFiler();
		Messager messager = this.processingEnv.getMessager();
		//Location projectPath1 = StandardLocation.NS_OUTPUT;//eclipse 不支持
		//Location projectPath2 = StandardLocation.PLATFORM_CLASS_PATH;//eclipse 不支持
		Location projectPath3 = StandardLocation.SOURCE_OUTPUT;//搜索现有的源文件的位置
		//Location projectPath4 = StandardLocation.CLASS_PATH;//eclipse 不支持
		FileObject fileobj = null;
		try {
			fileobj = filer.getResource(projectPath3, "", "");
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		//项目源码所在根目录
		String projectPath = fileobj.toUri().getPath().replace(".apt_generated", "");
		
		File file = new File(projectPath+override_dir);
		if(file.exists()){//重定义源码根目录
			String defineUrl = FileUtil.readString(file, "UTF-8");
			if(defineUrl.startsWith("/")) {
				defineUrl = defineUrl.substring(1);
			}
			if(defineUrl.endsWith("/")) {
				defineUrl = defineUrl.substring(0, defineUrl.length()-1);
			}
			projectPath += defineUrl;
		}else{
			projectPath += java_main_dir;
		}
		
		Set<Element> elements = null;
		List<File> files = null;
		for (TypeElement currentAnnotation : annotations) {
			Name qualifiedName = currentAnnotation.getQualifiedName();
			if (qualifiedName.contentEquals("com.lblin.method.annotation.Check")) {
				Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(currentAnnotation);
				elements = new LinkedHashSet<Element>();
				for (Element element : annotatedElements) {
					if(element instanceof ExecutableElement) {//只处理方法上的注解
						//Check v = element.getAnnotation(Check.class);
						elements.add(element);
					}else {
						String errMsg = "该注解@Check只可以标注在方法上";
						//messager = this.processingEnv.getMessager();
						messager.printMessage(Diagnostic.Kind.ERROR, errMsg, element);
					}
				}
			}else if (qualifiedName.contentEquals("com.lblin.method.annotation.HaveMethod")){
				Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(currentAnnotation);
				files = new LinkedList<File>();
				for (Element element : annotatedElements) {
					TypeElement typeElement = (TypeElement)element;
					/*全类名*/
		            String absolute = typeElement.getQualifiedName().toString();
					String fileUrl=projectPath+"/"+absolute.replace('.', '/')+".java";
					//messager = this.processingEnv.getMessager();
					File haveFile = new File(fileUrl);
					if(!haveFile.exists()) {
						String errMsg = "源码文件"+fileUrl+"不存在！";
						messager.printMessage(Diagnostic.Kind.ERROR, errMsg, element);
					}else {
						files.add(haveFile);
					}
		            
				}
			}
		}
		if(elements!=null&&elements.size()>0) {
			delayShow(elements, messager,files,projectPath);
		}
		return Boolean.TRUE;
	}
	
	/**
	 * 延迟显示提示
	 * @param elements
	 * @param messager
	 */
	public void delayShow(Set<Element> elements,Messager messager,List<File> files,String projectPath) {
		
		//加载@method注解标注的方法的调用链
		LoadClass.load(files,projectPath);
		
		Map<Element,Set<String>> traces = CheckMethodUser.checkMethodIsUse(elements);
		
		for(Element element:elements) {
			Set<String> trace = traces.get(element);
			
			StringBuilder sb = new StringBuilder("");
			if(trace.size()>0) {
				sb.append("当前方法共有").append(trace.size()).append("处调用\n")
				.append("------------------------------\n");
				for(String t : trace) {
					String[] ts = t.split(":");
					sb.append("调用类：\n").append(ts[0])
					.append("调用的方法名：\n").append(ts[1])
					.append("------------------------------\n");
				}
			}else {
				sb.append("当前方法没有被@Method注解的方法调用，请检查你的代码，避免冗余代码的发生！");
			}
			messager.printMessage(Diagnostic.Kind.ERROR, sb.toString(), element);
		}
		
		//LoadClass.destory();
	}
	
	public static void main(String[] args) {
	}

}
